﻿using System;
using System.IO;
using System.Net;
using System.Text;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Text.RegularExpressions;

namespace CFParser
{
    public partial class frmMain : Form
    {
        public frmMain()
        {
            InitializeComponent();
        }

        private readonly Hashtable Language = new Hashtable();
        private bool isStop = true;

        private static string CaptureTextBetween(string a, string b)
        {
            return "(?is)(?<=" + a + ").*?(?=" + b + ")";
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            if (btnStart.Text == "开始")
            {
                if (!Directory.Exists(txtPath.Text))
                {
                    Directory.CreateDirectory(txtPath.Text);
                }
                CheckForIllegalCrossThreadCalls = false;
                backgroundWorker.RunWorkerAsync();
                btnStart.Text = "停止";
                isStop = false;
            }
            else
            {
                isStop = true;
                btnStart.Text = "开始";
            }
        }

        private void AppendText(string text, Color color, bool isBold = true, bool addNewLine = true)
        {
            if (addNewLine)
            {
                text += Environment.NewLine;
            }
            txtContent.SelectionStart = txtContent.TextLength;
            txtContent.SelectionLength = 0;
            txtContent.SelectionColor = color;
            if (isBold) txtContent.SelectionFont = new Font("宋体", 9, FontStyle.Bold);
            txtContent.AppendText(text);
            txtContent.SelectionColor = txtContent.ForeColor;
            txtContent.ScrollToCaret();
        }

        private void CaptureCode()
        {
            txtContent.Clear();

            var pageNumber = 0;
            var pageCount = 50;
            var count = 1;

            while (pageCount == 50)
            {
                if (isStop) break;
                pageNumber++;
                pageCount = 0;
                // 获取当前页面的源代码
                var httpReq = (HttpWebRequest) WebRequest.Create("http://codeforces.com/submissions/" + txtUserName.Text + "/page/" + pageNumber);
                httpReq.Method = "GET";
                var httpResp = (HttpWebResponse) httpReq.GetResponse();
                httpReq.KeepAlive = false;
                var reader = new StreamReader(httpResp.GetResponseStream(), false);
                var respHtml = reader.ReadToEnd();
                httpReq.Abort();
           
                // 抓取所有的RunID
                var rgx = new Regex(CaptureTextBetween("data-submission-id=\"", "\""));
                var runId = rgx.Matches(respHtml);
                rgx = new Regex(CaptureTextBetween("/problemset/", "\">"));
                var contestId = rgx.Matches(respHtml);

                rgx = new Regex(CaptureTextBetween("<td>\r\n        ", "\r\n    </td>"));
                var lang = rgx.Matches(respHtml);

                rgx = new Regex(CaptureTextBetween("submissionVerdict=\"", "\""));
                var verdict = rgx.Matches(respHtml);
              
                for (var i = 0; i < runId.Count; i++)
                {
                    if (isStop) break;
                    var id = runId[i].ToString();
                    // 去除非法的匹配
                    if (id[0] < '0' || id[0] > '9') continue;
                    pageCount++;
                    var contest = contestId[i].ToString();
                    var vdt = verdict[i].ToString();
                    var l = lang[i].ToString();
                    if (vdt == "OK")
                    {
                        if (!chkAC.Checked) continue;
                        vdt = "Accepted";
                    }
                    if (vdt == "RUNTIME_ERROR")
                    {
                        if (!chkRE.Checked) continue;
                        vdt = "Runtime Error";
                    }
                    if (vdt == "WRONG_ANSWER")
                    {
                        if (!chkWA.Checked) continue;
                        vdt = "Wrong Answer";
                    }
                    if (vdt == "TIME_LIMIT_EXCEEDED")
                    {
                        if (!chkTLE.Checked) continue;
                        vdt = "Time Limit Exceeded";
                    }
                    if (vdt == "MEMORY_LIMIT_EXCEEDED")
                    {
                        if (!chkMLE.Checked) continue;
                        vdt = "Memory Limit Exceeded";
                    }
                    if (vdt == "COMPILATION_ERROR")
                    {
                        if (!chkCE.Checked) continue;
                        vdt = "Compilation Error";
                    }
                    if (vdt == "PRESENTATION_ERROR")
                    {
                        if (!chkPE.Checked) continue;
                        vdt = "Presentation Error";
                    }
                    if (vdt == "IDLENESS_LIMIT_EXCEEDED")
                    {
                        if (!chkILE.Checked) continue;
                        vdt = "Idleness Limit Exceeded";
                    }
                    if (vdt == "CHALLENGED")
                    {
                        if (!chkH.Checked) continue;
                        vdt = "Hacked";
                    }
                    
                    // 抓取代码
                    var isGym = contest[0] == 'g';
                    string newUrl, cid;
                    if (isGym)
                    {
                        // 如果是Gym的题目
                        contest = contest.Replace("gymProblem/", "");
                        var tmp = contest.Remove(contest.LastIndexOf('/'));         // 比赛编号
                        cid = contest.Replace(tmp + "/", "");
                        contest = contest.Replace("/" + cid, "");
                        newUrl = "http://codeforces.com/gym/" + contest + "/submission/" + id;
                    }
                    else
                    {
                        contest = contest.Replace("problem/", "");
                        var tmp = contest.Remove(contest.LastIndexOf('/'));         // 比赛编号
                        cid = contest.Replace(tmp + "/", "");
                        contest = contest.Replace("/" + cid, "");
                        newUrl = "http://codeforces.com/contest/" + contest + "/submission/" + id;
                    }
                    var sourceReq = (HttpWebRequest) WebRequest.Create(newUrl);
                    sourceReq.Method = "GET";
                   
                    var sourceResp = (HttpWebResponse) sourceReq.GetResponse();
                    sourceReq.KeepAlive = false;
                    reader = new StreamReader(sourceResp.GetResponseStream(), false);
                    respHtml = reader.ReadToEnd();
                    reader.Close();
                    sourceReq.Abort();
                    rgx =
                        new Regex(
                            CaptureTextBetween("<pre class=\"prettyprint program-source\" style=\"padding: 0.5em;\">",
                                "</pre>"));
                    var code = rgx.Matches(respHtml);
                    try
                    {
                        var source = code[0].ToString().Replace("\r\n", "\n");
                        source = source.Replace("\n", "\r\n");
                        source = "// Problem: " + contest + cid + "\r\n"
                            + "// Verdict: " + vdt + "\r\n" +
                            "// Language: " + l + "\r\n" + source;

                        source = source.Replace("&lt;", "<");
                        source = source.Replace("&gt;", ">");
                        source = source.Replace("&quot;", "\"");
                        source = source.Replace("&amp;", "&");
                        source = source.Replace("&#39;", "'");

                        string ext;
                        if (Language.Contains(l))
                        {
                            ext = (string) Language[l];
                        }
                        else
                        {
                            ext = ".unknown";
                        }

                        var fs = new FileStream(txtPath.Text + "\\" + id + ext, FileMode.Create);
                        var sw = new StreamWriter(fs, new UTF8Encoding(false));
                        sw.Write(source);
                        sw.Close();
                        fs.Close();
                        
                        // RunID
                        var sp = (int) Math.Log10(count) + 3;
                        var space = Spaces(sp);
                        AppendText(count++ + ". RunID = ", Color.Black, false, false);
                        AppendText(id, Color.Black);

                        // Problem ID
                        AppendText(space + "Problem ID = ", Color.Black, false, false);
                        AppendText(contest + cid, Color.DarkOrange);

                        // Language
                        AppendText(space + "Language = ", Color.Black, false, false);
                        AppendText(l, Color.Blue);

                        // Verdict
                        AppendText(space + "Problem Verdict = ", Color.Black, false, false);
                        Color clr;
                        if (vdt == "Accepted") clr = Color.Green;
                        else clr = Color.Red;
                        AppendText(vdt, clr);

                        txtContent.AppendText(Environment.NewLine);
                    }
                    catch (Exception)
                    {
                        // RunID
                        var sp = (int)Math.Log10(count) + 3;
                        var space = Spaces(sp);
                        AppendText(count++ + ". RunID = ", Color.Black, false, false);
                        AppendText(id, Color.Black);

                        // Problem ID
                        AppendText(space + "Problem ID = ", Color.Black, false, false);
                        AppendText(contest + cid, Color.DarkOrange);

                        // Language
                        AppendText(space + "Language = ", Color.Black, false, false);
                        AppendText(l, Color.Blue);

                        // Verdict
                        AppendText(space + "Problem Verdict = ", Color.Black, false, false);
                        Color clr;
                        if (vdt == "Accepted") clr = Color.Green;
                        else clr = Color.Red;
                        AppendText(vdt, clr);
                        AppendText(space + "Gym is unavailble to view the code.", Color.Red);
                        txtContent.AppendText(Environment.NewLine);
                        // txtContent.AppendText("    Code On: " + newUrl + "\r\n");
                    }
                    
                }
                reader.Close();
            }
        }

        private static string Spaces(int n)
        {
            var str = "";
            for (var i = 0; i < n; i++)
            {
                str += " ";
            }
            return str;
        }

        private void backgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
        {
            CaptureCode();
        }

        private void frmMain_Load(object sender, EventArgs e)
        {
            Language.Add("GNU C", ".c");
            Language.Add("GNU C++", ".cpp");
            Language.Add("GNU C++11", ".cpp");
            Language.Add("GNU C++0x", ".cpp");
            Language.Add("MS C++", ".cpp");
            Language.Add("Delphi", ".pas");
            Language.Add("FPC", ".pas");
            Language.Add("Tcl", ".tcl");
            Language.Add("PyPy 2", ".py");
            Language.Add("PyPy 3", ".py");
            Language.Add("Python 2", ".py");
            Language.Add("Python 3", ".py");
            Language.Add("MS C#", ".cs");
            Language.Add("Mono C#", ".cs");
            Language.Add("Go", ".go");
            Language.Add("Java 6", ".java");
            Language.Add("Java 7", ".java");
            Language.Add("Java 8", ".java");
        }

        private void btnBrowse_Click(object sender, EventArgs e)
        {
            var frmBrowse = new FolderBrowserDialog {ShowNewFolderButton = true};
            if (frmBrowse.ShowDialog() == DialogResult.OK)
            {
                txtPath.Text = frmBrowse.SelectedPath;
            }
        }
    }
}
